/**   
 * Copyright 2011 COMERE Harold
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package etrl.shaders;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * \class VariableModifier
 * \brief Class to modify some shader variables into a function
 * @author harold
 * 
 */
public class VariableModifier
{
	/**
	 * \enum VariableQualifier
	 * \brief Enumeration describing differents way to pass a variable to a glsl function
	 * @author harold
	 *
	 */
	public enum VariableQualifier
	{
		IN,		/** Variable value copied at function enter but not at function exit */
		OUT,	/** Variable value copied at function exit but not at function enter */
		INOUT	/** Variable value copied at function enter and exit */
	}

	/**
	 * The variable modifier name
	 */
	private String _name;

	/**
	 * The variable modifier source
	 */
	private String _source;

	/**
	 * The variable modifier shader stage
	 */
	private ShaderStage _stage;

	/**
	 * List of registered in-variables
	 */
	private ArrayList<Variable> _inVariables;

	/**
	 * List of registered out-variables
	 */
	private ArrayList<Variable> _outVariables;

	/**
	 * List of registered inout-variables
	 */
	private ArrayList<Variable> _inoutVariables;

	/**
	 * \brief Intializes the variable modifier
	 * @param[in] modifierName Name of the variable modifier
	 * @param[in] modifierSource Source of the variable modifier
	 * @param[in] stage Shader stage of the variable modifier
	 */
	public VariableModifier(String modifierName, String modifierSource, ShaderStage stage)
	{
		_source = modifierSource;
		_name = modifierName;
		_stage = stage;

		_inVariables = new ArrayList<Variable>();
		_outVariables = new ArrayList<Variable>();
		_inoutVariables = new ArrayList<Variable>();
	}

	/**
	 * \brief Gets the function call in the given shader stage
	 * @param stage Shader stage where to get modifier function call
	 * @return Function call
	 */
	public String getFunctionCall(ShaderStage stage)
	{
		if (_stage == stage)
		{
			String call = new String("\t" + _name + "_function(");

			boolean firstParam = true;
			for (Iterator<Variable> it = _inoutVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					call += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				call += var.nameWithPrefix(_stage);
			}

			for (Iterator<Variable> it = _inVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					call += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				call += var.nameWithPrefix(_stage);
			}

			for (Iterator<Variable> it = _outVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					call += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				call += var.nameWithPrefix(_stage);
			}

			call += ");\n";

			return call;
		}
		else
			return "";
	}

	/**
	 * \brief Gets the function implementation in the given shader stage
	 * @param stage Shader stage where to get modifier function implementation
	 * @return Function implementation
	 */
	public String getFunction(ShaderStage stage)
	{
		if (_stage == stage)
		{
			String function = new String("void " + _name + "_function (");

			boolean firstParam = true;

			for (Iterator<Variable> it = _inoutVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					function += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				function += "inout " + var.getType().asString() + " " + var.getNameWithArrayMark();
			}

			for (Iterator<Variable> it = _inVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					function += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				function += "in " + var.getType().asString() + " " + var.getNameWithArrayMark();
			}

			for (Iterator<Variable> it = _outVariables.iterator(); it.hasNext();)
			{
				if (!firstParam)
					function += ", ";
				else
					firstParam = false;

				Variable var = it.next();
				function += "out " + var.getType().asString() + " " + var.getNameWithArrayMark();
			}

			function += ")\n{\n\t" + _source + "\n}\n\n";

			return function;
		}
		else
			return "";
	}

	/**
	 * \brief Register a new variable in the modifier
	 * @param variable The variable to register
	 * @param qualifier The variable qualifier to use
	 */
	public void addVariable(Variable variable, VariableModifier.VariableQualifier qualifier)
	{
		if (variable.allowModifiers())
		{
			switch (qualifier)
			{
			case IN:
				_inVariables.add(variable);
				break;

			case OUT:
				_outVariables.add(variable);
				break;

			case INOUT:
				_inoutVariables.add(variable);
				break;

			default:
				System.out.println("[etrl::VariableModifier::addVariable] Cannot add this variable : Variable qualifier is not supported.");
				break;
			}
		}
		else
			System.out.println("[etrl::VariableModifier::addVariable] Cannot add this variable because it doesnt allow modifiers : " + variable.getName());
	}

}
